package com.example.demo.crc16;

import cn.hutool.core.util.ByteUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.dfa.WordTree;
import cn.hutool.json.JSONUtil;
import lombok.Data;

import javax.sound.midi.Soundbank;
import java.nio.ByteOrder;
import java.util.Arrays;

/**
 * <pre>
 *  描述:
 * </pre>
 *
 * @author godelgnis(wujiaer)
 * @version 1.0.0
 * @date 2021/12/1 14:09
 */
@Data
public class ServiceArgs{
    int length;
    int cmdId = 128;
    int instructionLength;
    int serverFlag = 1;
    String instruction;
    //int reserve = 1;
    int serialNumber = 1;
    int crcNumber;

    public static void main(String[] args) {

//"7878110108642570500007528039000200205C9A0D0A"
        int crc = CRC16.Crc16(HexUtil.decodeHex("11010864257050000752803900020020"));
        System.out.println(CRC16.bytesToHexStr(CRC16.intToBytes(crc)));

        printPrettyArgs("KWSET,0,MIP=219.144.245.178:9491#", "CA2F");

        // 20 平台
        printPrettyArgs("KWSET,0,MIP=219.144.245.178:19991#", "CA2F");
        // 42
        printPrettyArgs("KWSET,1,MIP=219.144.245.178:19920#", "CA2F");

        // 42
        printPrettyArgs("KWSET,1,MIP=219.144.245.178:19920#", "CA2F");


        printPrettyArgs("KWSET,0,MIP=device.qiguoiot.com:3378#", 8668, 8668);

        printPrettyArgs("KWSET,1,MIP=device.qiguoiot.com:3378#", 8668, 8668);

        printPrettyArgs("FN&&A&&测试&&15813324372&&&&&&&&##", "8524");
        printPrettyArgs("FN&&A&&u1&&13714836175&&&&&&&&##", "8524");
        printPrettyArgs("FN&&A&&13714836175&&13714836175&&&&&&&&##", "8524");
        printPrettyArgs("FN&&A&&文晓港&&18613048521&&文晓港&&17373728521&&123&&18613989681##", "8524");


        printPrettyArgs("FN&&A&&mw&&18613989681&&&&&&&&##", "8524");
        printPrettyArgs("FN&&A&&13714836175&&13714836715&&&&&&&&##", "8524");
        printPrettyArgs("WN&&A&&13714836715&&13588111597&&u2&&15029378233&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&##");
        printPrettyArgs("WN&&A&&孟&&18613989681&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&##");

        printPrettyArgs("SOS,A,13790774051,,#");
        printPrettyArgs("LJDW#");
        printPrettyArgs("FN&&A&&u1&&13714836715&&&&&&&&##", "8524");

//                               FN&&A&&u1&&13714836715&&&&&&&&##
        printPrettyArgs("KWSET,1,OFFLIMITED=1#");


        printPrettyArgs("SERVER,0,219.144.245.178,19991,0#");
        printPrettyArgs("KWSET,1,MIP=219.144.245.178:19991#");
        printPrettyArgs("TIMESET,2021,9,18,6,05,5#");
        printPrettyArgs("TIME|0|1|10:49-10:49|||||||]10:50-10:50|||||||]|||||||}");
        printPrettyArgs("TIME|0|1|09:12-12:00|||||||]|||||||]|||||||}");

        printPrettyArgs("LJDW#");

        printPrettyArgs("WN&&A&&u1&&13588111597&&u2&&15029378233&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&##");
        printPrettyArgs("WN&&A&&u1&&13588111597&&u2&&15029378233&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&##");
        printPrettyArgs("SERVER,0,47.119.125.153,21100,0#");
        printPrettyArgs("SERVER,0,219.144.245.178,9491,0#");
        printPrettyArgs("TIME|0|1|10:30-11:30|||||||]10:30-11:30|||||||]|||||||]}", 6);
        printPrettyArgs("Timer,1,30#");


        CRC16.simpleCommand("KWSET,0,MIP=219.144.245.178:9491#", 51759);
        CRC16.simpleCommand("Timer,1,30#");
        CRC16.simpleCommand("Timer,0,30#");
        CRC16.simpleCommand("LBSON#");
        CRC16.simpleCommand("LBSSW,OFF#");
        CRC16.simpleCommand("GTIMER#");
        CRC16.simpleCommand("GTIMER,2#");
        CRC16.simpleCommand("GPSON,6#");
        CRC16.simpleCommand("TIMER,10#");
        CRC16.simpleCommand("MODE,1#");
        CRC16.simpleCommand("SERVER,0,219.144.245.178,9491,0#");

        CRC16.hexStrToStrAndPrint("18 00 00 00 00 53 4f 53 2c 41 2c 2c 2c 31 35 38 32 31 34 39 31 36 32 32 23 00 01");
        CRC16.hexStrToStrAndPrint("25 00 00 CA 2F 4B 57 53 45 54 2C 30 2C 4D 49 50 3D 32 31 39 2E 31 34 34 2E 32 34 35 2E 31 37 38 3A 39 34 39 31 23");
        CRC16.hexStrToStrAndPrint("18 00 00 00 01 E6 8C 87 E4 BB A4 E6 A0 BC E5 BC 8F E9 94 99 E8 AF AF");
        CRC16.hexStrToStrAndPrint("2C 00 00 00 01 E5 BC 80 E5 90 AF 47 50 53 E5 B7 A5 E4 BD 9C 20 36 20 E5 88 86 E9 92 9F 20 E8 AE BE E7 BD AE E6 88 90 E5 8A 9F 00 00 01");
        CRC16.hexStrToStrAndPrint("81 1C 00 00 00 01 E8 AE BE E7 BD AE E6 88 90 E5 8A 9F 2E 47 54 49 4D 45 52 3A 32 00 00 01");
        CRC16.hexStrToStrAndPrint("81 0E 00 00 00 01 47 54 49 4D 45 52 3A 32 00 01");
        CRC16.hexStrToStrAndPrint("81 50 00 00 00 01 E8 AE BE E7 BD AE E6 88 90 E5 8A 9F 2E 4C 42 53 E6 95 B0 E6 8D AE E4 B8 8A E4 BC A0 E9 97 B4 E9 9A 94 3A 31 30 20 E5 88 86 E9 92 9F 2C 47 50 53 E6 95 B0 E6 8D AE E4 B8 8A E4 BC A0 E9 97 B4 E9 9A 94 3A 33 30 20 E7 A7 92 00 00 01");
        CRC16.hexStrToStrAndPrint("81 26 00 00 00 01 E8 AE BE E7 BD AE E6 88 90 E5 8A 9F 2E E5 AE 9A E6 97 B6 E5 AE 9A E4 BD 8D E6 A8 A1 E5 BC 8F 00");
        CRC16.hexStrToAsciiStrAndPrint(" 41 4C 4D 31 3D 43 34 3B 41 4C 4D 32 3D 43 43 3B 41 4C 4D 33 3D 34 43 " +
                "3B 53 54 41 31 3D 43 30 3B 44 59 44 3D 30 31 3B 53 4F 53 3D 2C 2C 3B 43 45 4E 54 45 52" +
                "3D 3B 46 45 4E 43 45 3D 46 65 6E 63 65 2C 4F 4E 2C 30 2C 32 33 2E 31 31 31 38 30 39 2C" +
                "31 31 34 2E 34 30 39 32 36 34 2C 34 30 30 2C 49 4E 20 6F 72 20 4F 55 54 2C 30 3B 4D 49 46" +
                "49 3D 4D 49 46 49 2C 4F 46 46");

        CRC16.hexStrToAsciiStrAndPrint("79 79 01 B2 80 FF 00 00 35 0F 57 4E 26 26 41 26 26 E6 B5 8B E8 AF 95 26 26 31 35 38 31 33 33 32 34 33 37 32 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 23 23 35 0F E5 F8 0D 0A");

    }
    static void printPrettyArgs(String cmd){
        printPrettyArgs(cmd, 1);
    }

    static void printPrettyArgs(String cmd, int serialNumber){
        printPrettyArgs(cmd, serialNumber, 1);
    }

    static void printPrettyArgs(String cmd, String hexSerialNumberAndServerFlag) {
        printPrettyArgs(cmd, hexSerialNumberAndServerFlag, hexSerialNumberAndServerFlag);
    }

    static void printPrettyArgs(String cmd, String hexSerialNumber, String hexServerFlag) {
        printPrettyArgs(cmd, HexUtil.hexToInt(hexSerialNumber), HexUtil.hexToInt(hexServerFlag));
    }

    static void printPrettyArgs(String cmd, int serialNumber, int serverFlag) {
        ServiceArgs build = ServiceArgs.build(cmd, serialNumber, serverFlag);
        System.out.println(JSONUtil.toJsonStr(build).replaceAll("\"", "\\\\\""));
        System.out.println(StrUtil.format(argTemplate, JSONUtil.parseObj(build)));
    }

    private static String argTemplate = "ServiceArgs详解：\n" +
            "{\n" +
            "    \"length\": {length}, // 包长度 = cmdId(1个字节) +  instructionLength数值 + instructionLength本身占位（1个字节） + 序列号（2个字节） + crcNumber(2个字节)\n" +
            "    \"cmdId\": {cmdId},\n" +
            "    \"instructionLength\": {instructionLength}, // 指令长度=服务器标志位(4个字节)+指令内容(M个字节)\n" +
            "    \"serverFlag\": {serverFlag},\n" +
            "    \"instruction\": \"{instruction}\",\n" +
            "    \"serialNumber\": {serialNumber},\n" +
            "    \"crcNumber\": {crcNumber}\n" +
            "}";

    static ServiceArgs build(String cmd, int serialNumber, int serverFlag) {
        ServiceArgs serviceArgs = new ServiceArgs();
        serviceArgs.setSerialNumber(serialNumber);
        serviceArgs.setServerFlag(serverFlag);
        serviceArgs.setInstruction(cmd);
        // 指令长度=服务器标志位(4个字节)+指令内容(M个字节)
        serviceArgs.setInstructionLength(4 + cmd.getBytes().length);
        // 包长度 = cmdId(1个字节) +  instructionLength数值 + instructionLength本身占位（1个字节） + 序列号（2个字节） + crcNumber(2个字节)
        serviceArgs.setLength(1 + (4 + cmd.getBytes().length) + 1 + 2 + 2);
        serviceArgs.setCrcNumber(genCommandByteCrc("80", serialNumber, serverFlag, cmd));
        return serviceArgs;
    }

//
//    private static int genCommandByteCrc(String cmdId, int seqNum, int flag, String msg) {
//        // 信息内容，包括指令长度+服务器标识位+内容
//        byte[] bodyBytes = genCommandByte(flag, msg);
//        // 拼接帧
//        byte[] frameBuf;
//        // 包长度值 = 协议号+信息内容+信息序列号+错误校验
//        int packetLen = 1 + bodyBytes.length + 2 + 2;
//        // 如果 信息内容 +（协议号+信息序列号+错误校验）>= 255, 包长度占2字节
//        int packetLenBytesLen = 1;
//        if (bodyBytes.length + (1 + 2 + 2) >= 255) {
//            packetLenBytesLen = 2;
//        }
//        // init frameBuf 起始位长度 + 包长度本身 + 包长度长度值 + 停止位长度
//        frameBuf = new byte[2 + packetLenBytesLen + packetLen + 2];
//        // 起始位不管，不影响crc计算
//
//        // packet length num 包长度值
//        byte[] packetLenBytes;
//        if (packetLenBytesLen > 1) {
//            byte[] tmp = ByteUtil.intToBytes(packetLen, ByteOrder.BIG_ENDIAN);
//            packetLenBytes = new byte[packetLenBytesLen];
//            System.arraycopy(tmp, tmp.length-packetLenBytesLen, packetLenBytes, 0, packetLenBytes.length);
//        } else {
//            packetLenBytes = new byte[]{(byte) packetLen};
//        }
//
//        System.arraycopy(packetLenBytes, 0, frameBuf, 2, packetLenBytesLen);
//
//        // cmd code 指令号
//        frameBuf[2 + packetLenBytes.length] = HexUtil.decodeHex(cmdId)[0];
//
//        // body 信息内容
//        System.arraycopy(bodyBytes, 0, frameBuf, 2 + packetLenBytesLen + 1, bodyBytes.length);
//
//        // seq num 信息序列号
//        byte[] seqNumBytes = ByteUtil.intToBytes(seqNum, ByteOrder.BIG_ENDIAN);
//        System.arraycopy(seqNumBytes, 2, frameBuf, 2 + packetLenBytesLen + 1 + bodyBytes.length, 2);
//
//        // crc16 crc校验位                  帧长度 - 起始位长度 - 错误校验长度 - 停止位长度
//        int crc16 = Crc16(frameBuf, 2, frameBuf.length - 2 - 2 -2);
//        byte[] crcBytes = ByteUtil.intToBytes(crc16, ByteOrder.BIG_ENDIAN);
//        System.arraycopy(crcBytes, 2, frameBuf, frameBuf.length - 4, 2);
//        frameBuf[frameBuf.length - 2] = 0x0D;
//        frameBuf[frameBuf.length - 1] = 0x0A;
//
//        printHexPacket(frameBuf);
//
//        return crc16;
//    }
//
//    private static void printHexPacket(byte[] buf) {
//        StringBuilder sb = new StringBuilder();
//        for (int i = 0; i < buf.length; i++) {
//            sb.append(CRC16.bytesToHexStr(buf[i]).toUpperCase());
//            sb.append(" ");
//        }
//        System.out.println(sb.toString());
//    }
//
//    private static byte[] genCommandByte(int flag, String msg) {
//        byte[] msgBytes = msg.getBytes();
//        int len = 4 + msgBytes.length;
//        byte[] buf = new byte[len + 1];
//        // 指令长度
//        // 协议号+指令长度（现在固定1字节）+服务器标识位+内容长度+信息序列号错误校验号 超过255，使用固定值0xFF, 抓奇果包，无语言/扩展位
//        buf[0] = 1 + 1 + 4 + msgBytes.length + 2 + 2 >= 255 ? (byte)0xFF : (byte)len;
//        // 服务器标志位
//        byte[] flagBytes = ByteUtil.intToBytes(flag, ByteOrder.BIG_ENDIAN);
//        System.arraycopy(flagBytes, 0, buf, 1, 4);
//        // 指令内容
//        System.arraycopy(msgBytes, 0, buf, 5, msgBytes.length);
//        return buf;
//    }




    // CRC16 / X25 多项式校验和
    public static int Crc16(byte [] bytes) {
        return Crc16(bytes, 0, bytes.length);
    }

    public static int Crc16(byte [] bytes, int startIdx, int len) {
        int i, j, lsb;
        int h = 0xffff;
        for (i = 0; i < len; i++) {
            h ^= (bytes[startIdx+i] & 0xff);
            h &= 0xffff;
            for (j = 0; j < 8; j++) {
                lsb = h & 0x0001; //取 CRC 的移出位
                h >>= 1;
                if (lsb == 1) {
                    h ^= 0x8408;
                    h &= 0xffff;
                }
            }
        }
        h ^= 0xffff;
        return h;
    }

    /**
     * 计算指令CRC
     * @param cmdId 指令号
     * @param seqNum 消息序列号
     * @param flag 服务器标识
     * @param msg 指令内容
     */
    public static int genCommandByteCrc(String cmdId, int seqNum, int flag, String msg) {
        byte[] bodyBytes = genCommandByte(flag, msg);
        // 拼接帧
        byte[] frameBuf;
        int bodyLen = bodyBytes.length + 5;
        byte[] bodyLenBytes;

        // init frameBuf
        frameBuf = new byte[4 + 1 + bodyLen];
        // start pos
        byte[] startDelimiterBytes = HexUtil.decodeHex("7878");
        System.arraycopy(startDelimiterBytes, 0, frameBuf, 0 , 2);
        // body length num
        frameBuf[2] = (byte) bodyLen;
        bodyLenBytes = new byte[]{(byte) bodyLen};

        // cmd code
        frameBuf[2 + bodyLenBytes.length] = HexUtil.decodeHex(cmdId)[0];

        // body
        System.arraycopy(bodyBytes, 0, frameBuf, 3 + bodyLenBytes.length, bodyBytes.length);

        // seq num
        byte[] seqNumBytes = ByteUtil.intToBytes(seqNum, ByteOrder.BIG_ENDIAN);
        System.arraycopy(seqNumBytes, 2, frameBuf, 3 + bodyLenBytes.length + bodyBytes.length, 2);

        // crc16
        int crc16 = Crc16(frameBuf, 2, frameBuf.length - 6);
        byte[] crcBytes = ByteUtil.intToBytes(crc16, ByteOrder.BIG_ENDIAN);
        System.arraycopy(crcBytes, 2, frameBuf, frameBuf.length - 4, 2);
        frameBuf[frameBuf.length - 2] = 0x0D;
        frameBuf[frameBuf.length - 1] = 0x0A;

        printHexPacket(frameBuf);

        return crc16;

    }

    private static void printHexPacket(byte[] buf) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < buf.length; i++) {
            sb.append(CRC16.bytesToHexStr(buf[i]).toUpperCase());
            sb.append(" ");
        }
        System.out.println(sb.toString());
    }

    public static byte[] genCommandByte(int flag, String msg) {
        byte[] msgBytes = msg.getBytes();
        int len = 4 + msgBytes.length;
        byte[] buf = new byte[len + 1];
        buf[0] = (byte)len;
        byte[] flagBytes = ByteUtil.intToBytes(flag, ByteOrder.BIG_ENDIAN);
        System.arraycopy(flagBytes, 0, buf, 1, 4);
        System.arraycopy(msgBytes, 0, buf, 5, msgBytes.length);
        return buf;
    }



    public static String parseStringFromAsciiBytes(byte[] data, int startIndex, int length) {
        try {
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < length; i++) {
                sb.append((char) data[startIndex+i]);
            }
            return sb.toString();
        } catch (Exception e) {
            return null;
        }
    }
}
